﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Logging;
using Sino.CapacityCloud.Repositories.Models;
using Sino.CapacityCloud.Services;
using Sino.CapacityCloud.Infrastructure.Filters;
using Microsoft.AspNet.Hosting;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Extensions.Caching.Distributed;
using Sino.CapacityCloud.WebApi.ViewModels;
using Sino.WebApi.Framework.Core.Filters;
using Sino.WebApi.Framework.Responses;

namespace Sino.CapacityCloud.WebApi.ApiControllers
{

    [SupressTokenValidation()]
    public class AccountController
        : ApiController<AccountController>
    {

        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly IEmailSender _emailSender;
        private readonly ISmsSender _smsSender;
        private readonly IRandomPasswordService _randomPasswordService;

        public AccountController(
           IHostingEnvironment env,
           IApplicationEnvironment applicationEnvironment,
           IDistributedCache cache,
           ITokenGenertationService tokenGenertationService,
           IRandomPasswordService randomPasswordService,
           IEmailSender emailSender,
           ISmsSender smsSender,
           ApplicationDbContext dbContext,
           ApplicationUserManager userManager,
           ApplicationRoleManager roleManager,
           ApplicationSignInManager signInManager,
           ILoggerFactory loggerFactory)
            : base(env, applicationEnvironment, cache, tokenGenertationService, dbContext, userManager, roleManager, signInManager, loggerFactory)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _emailSender = emailSender;
            _smsSender = smsSender;
            _randomPasswordService = randomPasswordService;
        }

        [HttpPost]
        //[AllowAnonymous]
        //[ValidateAntiForgeryToken]
        public async Task<JsonResult> Login([FromBody]LoginViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
                    if (result.Succeeded)
                    {
                        var user = await this.UserManager.FindByEmailAsync(model.Email);
                        var currentTime = DateTime.Now;
                        var token = await this._tokenGenertationService.GenerateAsync(currentTime);
                        user.Token = token;
                        await this.UserManager.UpdateAsync(user);
                        this._logger.LogInformation(1, $"User {model.Email} logged in.");
                        var response = new LoginResponse()
                        {
                            Info = new LoginInfo()
                            {
                                Token = token,
                                TokenExpirationDate = DateTime.Now.AddDays(1),
                                UserName = user.UserName,
                                UserId = user.Id,
                                RealNameState = (RealNameState)user.IdentityCardCertificationState,
                                HeadImage = user.AvatarKey,
                                Point = user.Points.FirstOrDefault(x => x.UserId == user.Id)?.TotalPoints ?? 0.0f
                            }
                        };
                        return await this.PackageResultAsync<LoginResponse>(response);
                    }
                    //if (result.RequiresTwoFactor)
                    //{
                    //    return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
                    //}
                    if (result.IsLockedOut)
                    {
                        _logger.LogWarning(2, "User account locked out.");
                        throw new UnauthorizedAccessException($"User account {model.Email} locked out.");
                    }
                    else
                    {
                        ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                        throw new UnauthorizedAccessException("Invalid login attempt.");
                    }
                }
                throw new UnauthorizedAccessException($"User account {model.Email} locked out.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to login because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<LoginResponse>(ex);
            }
        }

        [HttpPost]
        //[AllowAnonymous]
        //[ValidateAntiForgeryToken]
        public async Task<JsonResult> Register([FromBody]RegisterViewModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
                    var result = await _userManager.CreateAsync(user, model.Password);
                    if (result.Succeeded)
                    {
                        // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
                        // Send an email with this link
                        var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                        var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
                        await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
                            "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
                        //await _signInManager.SignInAsync(user, isPersistent: false);
                        _logger.LogInformation(3, "User created a new account with password.");
                        var currentTime = DateTime.Now;
                        var token = await this._tokenGenertationService.GenerateAsync(currentTime);
                        user.Token = token;
                        await this.UserManager.UpdateAsync(user);
                        var response = new UserRegistrationResultResponse()
                        {
                            IsEmailConfirmRequired = true,
                            IsScucess = true,
                            LoginInfo = new LoginInfo()
                            {
                                Token = token,
                                TokenExpirationDate = DateTime.Now.AddDays(1),
                                UserName = user.UserName,
                                UserId = user.Id,
                                RealNameState = (RealNameState)user.IdentityCardCertificationState,
                                HeadImage = user.AvatarKey,
                                Point = user.Points.FirstOrDefault(x => x.UserId == user.Id)?.TotalPoints ?? 0.0f
                            }
                        };
                        return await this.PackageResultAsync<UserRegistrationResultResponse>(response);
                    }
                    throw new Exception(string.Join(Environment.NewLine, result.ToErrorStrings()));
                }
                throw new ArgumentException("Your input is invalid.");
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to register because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<UserRegistrationResultResponse>(ex);
            }
        }

        [HttpGet]
        //[ValidateAntiForgeryToken]
        public async Task<JsonResult> LogOff(string token)
        {
            try
            {
                if (HttpContext.User.IsSignedIn())
                {
                    await _signInManager.SignOutAsync();
                }
                if (!string.IsNullOrEmpty(token))
                {
                    var user = this.DataContext.Users.FirstOrDefault(x => x.Token == token);
                    if (user != null)
                    {
                        user.Token = "";
                        this.DataContext.SaveChanges();
                    }
                }
                _logger.LogInformation(4, "User logged out.");
                return await this.PackageResultAsync<VoidResponse>();
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Failed to register because: {ex.Message + Environment.NewLine + ex.StackTrace}");
                return await this.PackageResultAsync<VoidResponse>(ex);
            }
        }

        [HttpGet]
        //[AllowAnonymous]
        public async Task<IActionResult> ConfirmEmail(string userId, string code)
        {
            if (userId == null || code == null)
            {
                return View("Error");
            }
            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
            {
                return View("Error");
            }
            var result = await _userManager.ConfirmEmailAsync(user, code);
            return View(result.Succeeded ? "ConfirmEmail" : "Error");
        }

        [HttpPost]
        //[AllowAnonymous]
        //[ValidateAntiForgeryToken]
        public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = await _userManager.FindByNameAsync(model.Email);
                if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
                {
                    // Don't reveal that the user does not exist or is not confirmed
                    return View("ForgotPasswordConfirmation");
                }

                // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
                // Send an email with this link
                //var code = await _userManager.GeneratePasswordResetTokenAsync(user);
                //var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
                //await _emailSender.SendEmailAsync(model.Email, "Reset Password",
                //   "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
                //return View("ForgotPasswordConfirmation");
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // GET: /Account/ForgotPasswordConfirmation
        [HttpGet]
        [AllowAnonymous]
        public IActionResult ForgotPasswordConfirmation()
        {
            return View();
        }

        //
        // GET: /Account/ResetPassword
        [HttpGet]
        [AllowAnonymous]
        public IActionResult ResetPassword(string code = null)
        {
            return code == null ? View("Error") : View();
        }

        //
        // POST: /Account/ResetPassword
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }
            var user = await _userManager.FindByNameAsync(model.Email);
            if (user == null)
            {
                // Don't reveal that the user does not exist
                return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
            }
            var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
            if (result.Succeeded)
            {
                return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
            }
            AddErrors(result);
            return View();
        }

        //
        // GET: /Account/ResetPasswordConfirmation
        [HttpGet]
        [AllowAnonymous]
        public IActionResult ResetPasswordConfirmation()
        {
            return View();
        }

        #region Helpers
        private void AddErrors(IdentityResult result)
        {
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }

        private async Task<ApplicationUser> GetCurrentUserAsync()
        {
            return await _userManager.FindByIdAsync(HttpContext.User.GetUserId());
        }

        private IActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        #endregion

    }

}